#include "cond_var.h"

#include <assert.h>
#include <errno.h>

namespace tinySrv
{

CondVar::CondVar()
{
  if (pthread_mutex_init(&Mtx, 0) == -1)
    throw CondVarException("Failed to create restrict mutex");
  if (pthread_cond_init(&Cond, 0) == -1)
  {
    int Res = pthread_mutex_destroy(&Mtx);
    assert(Res != -1 && "Failed to destroy restrict mutex");
    throw CondVarException("Failed to create conditional variable");
  }
}

CondVar::~CondVar()
{
  try
  {
    AlarmForAll();
  }
  catch (std::exception const&)
  {
    // TODO:
  }
  int Res = pthread_cond_destroy(&Cond);
  assert(Res != -1 && "Failed to destroy conditional variable");
  Res = pthread_mutex_destroy(&Mtx);
  assert(Res != -1 && "Failed to destroy restrict mutex");
}

void CondVar::Alarm()
{
  if (pthread_cond_signal(&Cond) == -1)
    throw CondVarException("Alarm is failed");
}

void CondVar::AlarmForAll()
{
  if (pthread_cond_broadcast(&Cond) == -1)
    throw CondVarException("Alarm for all failed");
}

void CondVar::Wait()
{
  if (pthread_mutex_lock(&Mtx) == -1)
    throw CondVarException("Failed to lock mutex");
  if (pthread_cond_wait(&Cond, &Mtx) == -1)
  {
    if (pthread_mutex_unlock(&Mtx) == -1)
      throw CondVarException("Failed to wait conditional variable and failed to unlock mutex");
    throw CondVarException("Failed to wait conditional variable");
  }
  if (pthread_mutex_unlock(&Mtx) == -1)
    throw CondVarException("Failed to unlock mutex");
}

bool CondVar::WaitTimeout(unsigned long timeout)
{
  timespec TS;
  if (clock_gettime(CLOCK_REALTIME, &TS) == -1)
    throw CondVarException("Failed to get current time");
  TS.tv_sec += timeout / 1000;
  TS.tv_nsec += (timeout - 1000 * (timeout / 1000)) * 1000000;  
  if (pthread_mutex_lock(&Mtx) == -1)
    throw CondVarException("Failed to lock mutex");
  int Result = pthread_cond_timedwait(&Cond, &Mtx, &TS);
  bool Ret = true;
  if (Result)
  {
    if (errno == ETIMEDOUT)
      Ret = false;
    else
    {
      if (pthread_mutex_unlock(&Mtx) == -1)
        throw CondVarException("Failed to wait conditional variable and failed to unlock mutex");
      throw CondVarException("Failed to wait conditional variable");
    }
  }
  if (pthread_mutex_unlock(&Mtx) == -1)
    throw CondVarException("Failed to unlock mutex");
  return Ret;
}

}
